2 * Adium is the legal property of its developers, whose names are listed in the copyright file included
3 * with this source distribution.
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11 * Public License for more details.
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 #import "XtrasInstaller.h"
18 #import <AIUtilities/CBApplicationAdditions.h>
19 #import <AIUtilities/ESBundleAdditions.h>
20 #import <AIUtilities/AIExceptionHandlingUtilities.h>
21 #import "NSString_UUID.h"
23 //Should only be YES for testing
24 #define ALLOW_UNTRUSTED_XTRAS NO
26 @interface XtrasInstaller (PRIVATE)
27 - (void)closeInstaller;
31 * @class XtrasInstaller
32 * @brief Class which displays a progress window and downloads an AdiumXtra, decompresses it, and installs it.
34 @implementation XtrasInstaller
36 //XtrasInstaller does not autorelease because it will release itself when closed
37 + (XtrasInstaller *)installer
39 return [[XtrasInstaller alloc] init];
44 if ((self = [super init])) {
49 if(![NSApp isURLLoadingAvailable]) {
63 - (IBAction)cancel:(id)sender;
65 if (download) [download cancel];
66 [self closeInstaller];
69 - (void)sheetDidDismiss:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
74 - (void)closeInstaller
76 if (window) [window close];
80 - (void)installXtraAtURL:(NSURL *)url
82 if ([[url host] isEqualToString:@"www.adiumxtras.com"] || ALLOW_UNTRUSTED_XTRAS) {
85 [NSBundle loadNibNamed:@"XtraProgressWindow" owner:self];
86 [progressBar setUsesThreadedAnimation:YES];
88 [progressBar setDoubleValue:0];
89 [percentText setStringValue:@"0%"];
90 [cancelButton setLocalizedString:AILocalizedString(@"Cancel",nil)];
91 [window setTitle:AILocalizedString(@"Xtra Download",nil)];
92 [window makeKeyAndOrderFront:self];
94 urlToDownload = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"%@://%@/%@?%@", @"http", [url host], [url path], [url query]]];
95 // dest = [NSTemporaryDirectory() stringByAppendingPathComponent:[[urlToDownload path] lastPathComponent]];
97 Class NSURLDownloadClass = NSClassFromString(@"NSURLDownload");
98 Class NSURLRequestClass = NSClassFromString(@"NSURLRequest");
99 download = [[NSURLDownloadClass alloc] initWithRequest:[NSURLRequestClass requestWithURL:urlToDownload] delegate:self];
100 // [download setDestination:dest allowOverwrite:YES];
102 [urlToDownload release];
105 NSRunAlertPanel(AILocalizedString(@"Nontrusted Xtra", nil),
106 AILocalizedString(@"This Xtra is not hosted by adiumxtras.com. Automatic installation is not allowed.", nil),
107 AILocalizedString(@"Cancel", nil),
109 [self closeInstaller];
113 - (void)download:(NSURLDownload *)connection didReceiveResponse:(NSURLResponse *)response
115 amountDownloaded = 0;
116 downloadSize = [response expectedContentLength];
117 [progressBar setMaxValue:(long long)downloadSize];
118 [progressBar setDoubleValue:0.0];
121 - (void)download:(NSURLDownload *)connection decideDestinationWithSuggestedFilename:(NSString *)filename
123 NSString * downloadDir = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString uuid]];
124 [[NSFileManager defaultManager] createDirectoryAtPath:downloadDir attributes:nil];
125 dest = [downloadDir stringByAppendingPathComponent:filename];
126 [download setDestination:dest allowOverwrite:YES];
129 - (void)download:(NSURLDownload *)download didReceiveDataOfLength:(unsigned)length
131 amountDownloaded += (long long)length;
132 if (downloadSize != NSURLResponseUnknownLength) {
133 [progressBar setDoubleValue:(double)amountDownloaded];
134 [percentText setStringValue:[NSString stringWithFormat:@"%f%",(double)((amountDownloaded / (double)downloadSize) * 100)]];
137 [progressBar setIndeterminate:YES];
140 - (BOOL)download:(NSURLDownload *)download shouldDecodeSourceDataOfMIMEType:(NSString *)encodingType {
144 - (void)download:(NSURLDownload *)inDownload didFailWithError:(NSError *)error {
147 errorMsg = [NSString stringWithFormat:AILocalizedString(@"An error occurred while downloading this Xtra: %@.",nil),[error localizedDescription]];
149 NSBeginAlertSheet(AILocalizedString(@"Xtra Downloading Error",nil), AILocalizedString(@"Cancel",nil), nil, nil, window, self,
150 NULL, @selector(sheetDidDismiss:returnCode:contextInfo:), nil, errorMsg);
153 - (void)downloadDidFinish:(NSURLDownload *)download {
154 NSArray *fileNames = nil;
155 NSString *lastPathComponent = [[dest lowercaseString] lastPathComponent];
156 NSString *pathExtension = [lastPathComponent pathExtension];
157 BOOL decompressionSuccess = YES;
159 if ([pathExtension isEqualToString:@"tgz"] || [lastPathComponent hasSuffix:@".tar.gz"]) {
160 NSTask *uncompress, *untar;
162 uncompress = [[NSTask alloc] init];
163 [uncompress setLaunchPath:@"/usr/bin/gunzip"];
164 [uncompress setArguments:[NSArray arrayWithObjects:@"-df" , [dest lastPathComponent] , nil]];
165 [uncompress setCurrentDirectoryPath:[dest stringByDeletingLastPathComponent]];
170 [uncompress waitUntilExit];
174 decompressionSuccess = NO;
178 [uncompress release];
180 if (decompressionSuccess) {
181 if ([pathExtension isEqualToString:@"tgz"]) {
182 dest = [[dest stringByDeletingPathExtension] stringByAppendingPathExtension:@"tar"];
185 dest = [dest substringToIndex:[dest length] - 3];//remove the .gz, leaving us with .tar
188 untar = [[NSTask alloc] init];
189 [untar setLaunchPath:@"/usr/bin/tar"];
190 [untar setArguments:[NSArray arrayWithObjects:@"-xvf", [dest lastPathComponent], nil]];
191 [untar setCurrentDirectoryPath:[dest stringByDeletingLastPathComponent]];
196 [untar waitUntilExit];
200 decompressionSuccess = NO;
206 } else if ([pathExtension isEqualToString:@"zip"]) {
209 //First, perform the actual unzipping
210 unzip = [[NSTask alloc] init];
211 [unzip setLaunchPath:@"/usr/bin/unzip"];
212 [unzip setArguments:[NSArray arrayWithObjects:
213 @"-o", /* overwrite */
215 dest, /* source zip file */
216 @"-d", [dest stringByDeletingLastPathComponent], /*destination folder*/
219 [unzip setCurrentDirectoryPath:[dest stringByDeletingLastPathComponent]];
224 [unzip waitUntilExit];
228 decompressionSuccess = NO;
234 decompressionSuccess = NO;
237 NSFileManager * fileManager = [NSFileManager defaultManager];
238 //Delete the compressed xtra, now that we've decompressed it
239 [fileManager removeFileAtPath:dest handler:nil];
241 dest = [dest stringByDeletingLastPathComponent];
243 //the remaining files in the directory should be the contents of the xtra
244 fileNames = [fileManager directoryContentsAtPath:dest];
245 AILog(@"Downloaded to %@. fileNames: %@",dest,fileNames);
247 if (decompressionSuccess && fileNames) {
248 NSEnumerator *fileEnumerator;
251 NSSet *supportedDocumentExtensions = [[NSBundle mainBundle] supportedDocumentExtensions];
253 fileEnumerator = [fileNames objectEnumerator];
254 while((nextFile = [fileEnumerator nextObject]))
256 NSString *fileExtension = [nextFile pathExtension];
257 NSEnumerator *supportedDocumentExtensionsEnumerator;
259 BOOL isSupported = NO;
261 //We want to do a case-insensitive path extension comparison
262 supportedDocumentExtensionsEnumerator = [supportedDocumentExtensions objectEnumerator];
263 while (!isSupported &&
264 (extension = [supportedDocumentExtensionsEnumerator nextObject])) {
265 isSupported = ([fileExtension caseInsensitiveCompare:extension] == NSOrderedSame);
271 xtraPath = [dest stringByAppendingPathComponent:nextFile];
273 //Open the file directly
274 AILog(@"Installing %@",xtraPath);
275 success = [[NSApp delegate] application:NSApp
276 openTempFile:xtraPath];
279 NSLog(@"Installation Error: %@",xtraPath);
285 NSLog(@"Installation Error: %@ (%@)",dest, (decompressionSuccess ? @"Decompressed succesfully" : @"Failed to decompress"));
288 //delete our temporary directory, and any files remaining in it
289 [fileManager removeFileAtPath:[dest stringByDeletingLastPathComponent] handler:nil];
291 [self closeInstaller];